package com.huangtang.pc.aspect;

import cn.hutool.core.util.StrUtil;
import com.huangtang.pc.annotation.RateLimit;
import com.huangtang.pc.constant.CommonConstant;
import com.huangtang.pc.exception.BizException;
import com.huangtang.pc.result.Result;
import com.huangtang.pc.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * @author sunjf
 * @date 2024/01/17
 */
@Slf4j
@Aspect
@Component
public class RateLimitAspect {


    private final RedissonClient redissonClient;

    @Autowired(required = false)
    public RateLimitAspect(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }


    @Pointcut("@annotation(com.huangtang.pc.annotation.RateLimit)")
    public void pointcut() {
    }

    @Before(value = "pointcut()")
    public void before(JoinPoint jp) {

        HttpServletRequest request =
                ((ServletRequestAttributes)
                        Objects.requireNonNull(RequestContextHolder.getRequestAttributes()))
                        .getRequest();

        MethodSignature methodSignature = (MethodSignature) jp.getSignature();
        Method method = methodSignature.getMethod();

        RateLimit rateLimit = method.getAnnotation(RateLimit.class);

        int limit = rateLimit.limit();
        int time = rateLimit.time();

        // 获取ip地址 和 访问的uri
        String ip = request.getRemoteAddr().replace(":", ",");
        String uri = request.getRequestURI();
        // 缓存key
        String key = CommonConstant.CACHE_KEY.RATE_LIMIT + ip + ":" + uri;

        RLock lock = redissonClient.getLock(key);
        lock.lock();
        log.info("加锁成功:{}",lock.getName());
        try{
            // 判断缓存key是否存在
            if(RedisUtil.hasKey(key)){
                if( (int)RedisUtil.get(key) < limit){
                    RedisUtil.incr(key, 1);
                }else{
                    log.warn("ip:{},访问:{}过于频繁，请稍后再试",ip,uri);
                    throw new BizException("访问频繁，请稍后再试");
                }
            }else{
                RedisUtil.set(key, 1, time, rateLimit.timeUnit());
            }
        }catch (Exception e){
            log.error("加锁失败,{}",e.getMessage());
            throw new BizException(e.getMessage());
        }finally {
            log.info("释放锁,{}",lock.getName());
            lock.unlock();
        }
    }

}
